package org.arclib;

// Logger 
import org.apache.log4j.Logger;
import org.arclib.draw.Color;
import org.arclib.math.Point;
import org.arclib.math.Size;

// LWJGL
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

/**
 * Handles the creation of a window and operations to be performed on a Window
 * 
 * @author Clay Smith
 * 
 */
public class Window
{
	/**
	 * 
	 * Creates a new window
	 * 
	 * @param argT
	 *            Window Title
	 * @param argW
	 *            Window Width
	 * @param argH
	 *            Window Height
	 * @param argFS
	 *            If Window is fullscreen (true) or not
	 * 
	 */
	public Window(String argT, Size argS, boolean argFS)
	{
		title = argT;
		size = argS;
		fullscreen = argFS;

		log.info("open(" + argT + ", " + argS.getW() + ", " + argS.getH()
				+ ", " + argFS + ");");

		try
		{
			mode = findDisplayMode((int) size.getW(), (int) size.getH(),
					Display.getDisplayMode().getBitsPerPixel());
			Display.setDisplayMode(mode);
			Display.setTitle(argT);

			// start of in windowed mode
			Display.create();
			resizeGL();
			setBackgroundColor(Color.Black);

			// sync frame (only works on windows)
			Display.setVSyncEnabled(true);

		} catch (Exception e)
		{
			log.error(e.toString());
		}
	}

	// find display mode
	private DisplayMode findDisplayMode(int width, int height, int bpp)
			throws LWJGLException
	{
		DisplayMode[] modes = Display.getAvailableDisplayModes();

		for (int i = 0; i < modes.length; i++)
		{
			if (modes[i].getWidth() == width && modes[i].getHeight() == height
					&& modes[i].getBitsPerPixel() >= bpp
					&& modes[i].getFrequency() <= 60)
			{
				return modes[i];
			}
		}

		log.error("Could not find correct display mode");

		return Display.getDesktopDisplayMode();
	}

	private void setGLStates()
	{
		GL11.glEnable(GL11.GL_TEXTURE_2D);
		GL11.glEnable(GL11.GL_BLEND);
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
		GL11.glDisable(GL11.GL_DEPTH_TEST);
	}

	// setup the projection and modelview matrices
	static void setupGLMatrices()
	{
		// projection matrix
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		GL11.glOrtho(0, size.getW(), 0, size.getH(), -1.0f, 1.0f);

		// modelview matrix
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();

		GL11.glViewport(0, 0, (int) size.getW(), (int) size.getH());
	}

	/**
	 * 
	 * Resize the window
	 * 
	 * @param argW
	 *            Window width to resize to
	 * @param argH
	 *            Window height to resize to
	 */
	public void resize(int argW, int argH)
	{
		mode = new DisplayMode(argW, argH);

		try
		{
			if (fullscreen == true)
			{
				Display.setDisplayModeAndFullscreen(mode);
			} else
			{
				Display.setDisplayMode(mode);
			}
		} catch (Exception e)
		{
			log.error(e.toString());
		}

		resizeGL();
	}

	/**
	 * Toggles to fullscreen or windowed mode
	 */
	public void toggleFullScreen()
	{
		fullscreen = !fullscreen;
		try
		{
			Display.setFullscreen(fullscreen);
		} catch (LWJGLException e)
		{
			log.error(e.toString());
		}
	}

	private void resizeGL()
	{
		// viewport
		GL11.glViewport(0, 0, (int) size.getW(), (int) size.getH());

		// reset the matrices
		setupGLMatrices();

		// states
		setGLStates();
	}

	void setBackgroundColor(Color argC)
	{
		// background color
		GL11.glClearColor(argC.getR(), argC.getG(), argC.getB(), argC.getA());
	}

	/**
	 * 
	 * @return title Window title
	 */
	public String getTitle()
	{
		return title;
	}

	/**
	 * Return width
	 * 
	 * @return width Window size
	 */
	public Size getSize()
	{
		return size;
	}

	/**
	 * If window is full screen or not
	 * 
	 * @return fullscreen boolean
	 */
	public static boolean isFullscreen()
	{
		return fullscreen;
	}

	/**
	 * Get LWJGL display mode
	 * 
	 * @return mode
	 */
	public static DisplayMode getMode()
	{
		return mode;
	}

	/**
	 * Destroy window context
	 */
	public static void destroy()
	{
		Display.destroy();
	}

	public static void clear()
	{
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
	}

	public static void swap()
	{
		Display.update();
	}

	private static String title;
	private static Size size;
	private static boolean fullscreen;
	private static Logger log = Logger.getLogger(Window.class);
	private static DisplayMode mode;

	public static int getWidth()
	{
		// TODO Auto-generated method stub
		return (int) size.w;
	}

	public static int getHeight()
	{
		return (int) size.h;
	}
}
